import json
from decimal import Decimal

from django import http
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.paginator import Paginator, EmptyPage
from django.db import transaction
from django.shortcuts import render
from django.utils import timezone
from django.views import View
from django_redis import get_redis_connection

from apps.goods.models import SKU
from apps.orders.models import OrderInfo, OrderGoods
from apps.users.models import Address
from utils.response_code import RETCODE
import logging

logger = logging.getLogger('django')

'''
前端逻辑
    订单界面必须是登录用户访问, 需要判断用户是否登陆

后端逻辑
    1. 登陆用户才能访问订单界面
    2. 收货地址的获取
    3. 支付方式的选择
    4. 购物车选中的商品列表的获取
        4.1 连接redis
        4.2 hash 商品id 和 数量
        4.3 set 选中的商品id
        4.4 重新组织一个选中的商品信息字典
    5. 结算总金额

请求方式和路径
GET		  orders/settlement/
'''


class OrderSettlementView(LoginRequiredMixin, View):
    def get(self, request):
        # 1. 登陆用户才能访问订单界面
        user = request.user
        # 2. 收货地址的获取
        try:
            addresses = Address.objects.filter(id=user.id, is_deleted=False)
        except Address.DoesNotExist:
            # 如果地址为空，渲染模板时会判断，并跳转到地址编辑页面
            addresses = None

            # 3. 支付方式的选择(一般是写死的, 不需要后端展示)
            # 4. 购物车选中的商品列表的获取
            # 4.1 连接redis
        redis_conn = get_redis_connection('carts')
        # 4.2 hash 商品id 和 数量
        redis_cart = redis_conn.hgetall('carts_%s' % user.id)
        # 4.3 set 选中的商品id
        redis_selected = redis_conn.smembers('selected_%s' % user.id)
        # 4.4 重新组织一个选中的商品信息字典
        cart = {}
        for sku_id in redis_selected:
            cart[int(sku_id)] = int(redis_cart[sku_id])

        # 5. 结算总金额
        # 准备初始值总数量和总金额
        from decimal import Decimal
        total_count = 0
        total_amount = Decimal('0')

        # 查询商品信息
        skus = SKU.objects.filter(id__in=cart.keys())
        for sku in skus:
            # 商品列表展示结算商品数量
            sku.count = cart[sku.id]
            # 商品列表展示结算商品价格
            sku.amount = sku.count * sku.price

            # 计算总数量和总金额
            total_count += sku.count
            total_amount += sku.count * sku.price
        # 补充运费
        if total_amount >= Decimal('99.00'):
            freight = Decimal(0.00)
        else:
            freight = Decimal('10.00')

        # 组织渲染界面数据
        context = {
            'addresses': addresses,
            'skus': skus,
            'total_count': total_count,
            'total_amount': total_amount,
            'freight': freight,
            'payment_amount': total_amount + freight,
            'username': user.username
        }

        # 返回响应
        return render(request, 'place_order.html', context)


'''
前端逻辑
传递用户信息, 收货地址, 支付方式, 商品列表, 总金额

后端逻辑
    1. 接收数据
    2. 验证数据
    3. 数据入库
        1. 订单信息 OrderInfo
            1.1 组织oder_id
            1.2 组织user
            1.3 组织地址信息
            1.4 组织总数量 总金额 运费
            1.5 支付信息
            1.6 订单状态

    2. 订单商品信息 OrderGoods
        2.1 连接redis, 获取redis数据
        2.2 将获取的数据进行字典转换
        2.3 对商品进行遍历
            判断商品库存是否充足
            充足则库存减少, 销量增加
            保存订单信息
            对总数量, 总金额进行累加

    3.更新的订单信息(总金额)
    4. 清除购物车内数据
    5. 返回响应

请求方式和路径
POST          orders/commit/
'''


class OrderCommitView(LoginRequiredMixin, View):
    def post(self, request):
        # 1. 接收数据
        json_dict = json.loads(request.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = int(json_dict.get('pay_method'))
        # 2. 验证数据
        if not all([address_id, pay_method]):
            return http.HttpResponseBadRequest('缺少必传参数')

        # 验证address_id
        try:
            address = Address.objects.get(id=address_id)
        except Address.DoesNotExist:
            return http.HttpResponseBadRequest('参数address_id错误')

        # 验证pay_method
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']]:
            return http.HttpResponseBadRequest('参数pay_method错误')

        # 3. 数据入库
        # 1. 订单信息 OrderInfo
        # 获取登陆用户
        user = request.user
        # 生成订单编号: 年月日时分秒+用户编号
        order_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)

        # 开启事务
        with transaction.atomic():
            # 创建事务保存点
            save_id = transaction.savepoint()

            # 暴力回滚
            try:
                # 保存订单基本信息
                order = OrderInfo.objects.create(
                    # 1.1 组织order_id
                    order_id=order_id,
                    # 1.2 组织user
                    user=user,
                    # 1.3 组织地址信息
                    address=address,
                    # 1.4 组织总数量 总金额 运费
                    total_count=0,
                    total_amount=Decimal('0'),
                    freight=Decimal('10'),
                    # 1.5 支付信息
                    pay_method=pay_method,
                    # 1.6 订单状态, 如果到付, 那就是待收货, 如果是支付宝, 那就是待付款
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM[
                        'ALIPAY'] else
                    OrderInfo.ORDER_STATUS_ENUM['UNSEND']
                )

                # 2. 订单商品信息 OrderGoods
                # 2.1 连接redis, 获取redis数据
                redis_conn = get_redis_connection('carts')
                redis_cart = redis_conn.hgetall('carts_%s' % user.id)
                redis_selected = redis_conn.smembers('selected_%s' % user.id)

                # 2.2 将获取的数据进行字典转换
                carts = {}
                for sku_id in redis_selected:
                    carts[int(sku_id)] = int(redis_cart[sku_id])
                sku_ids = carts.keys()

                # 2.3 对商品进行遍历
                for sku_id in sku_ids:
                    # 查询SKU信息
                    sku = SKU.objects.get(id=sku_id)

                    # 读取原始库存
                    origin_stock = sku.stock
                    origin_sales = sku.sales

                    # 判断商品库存是否充足
                    sku_count = carts[sku.id]
                    if sku_count > origin_stock:
                        # 出错就回滚
                        transaction.savepoint_rollback(save_id)
                        return http.JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '库存不足'})

                    # 模拟多人抢购
                    # import time
                    # time.sleep(5)

                    # 充足则库存减少, 销量增加
                    # sku.stock -= sku_count
                    # sku.sales += sku_count
                    # sku.save()

                    # 使用乐观锁更新库存和销量
                    new_stock = origin_stock - sku_count
                    new_sales = origin_sales + sku_count
                    result = SKU.objects.filter(id=sku.id, stock=origin_stock).update(stock=new_stock, sales=new_sales)
                    # rect 返回1 表示成功 返回0表示失败
                    if result == 0:
                        transaction.savepoint_rollback(save_id)
                        return http.JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '库存不足'})

                    # 保存订单信息
                    OrderGoods.objects.create(
                        # 保存订单信息
                        order=order,
                        # 保存商品信息
                        sku=sku,
                        # 保存数量
                        count=sku_count,
                        # 保存单价
                        price=sku.price
                    )

                    # 对总数量, 总金额进行累加
                    order.total_count += sku_count
                    order.total_amount += (sku_count * sku.price)

                # 3.更新的订单信息(总金额)
                order.total_amount += order.freight
                order.save()
            except Exception as e:
                logger.error(e)
                # 有问题就回滚
                transaction.savepoint_rollback(save_id)
                return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '下单失败'})

        # 没问题就提交订单
        transaction.savepoint_commit(save_id)

        # 4. 清除购物车已结算的商品
        pl = redis_conn.pipeline()
        pl.hdel('carts_%s' % user.id, *redis_selected)
        pl.srem('selected_%s' % user.id, *redis_selected)
        pl.execute()

        # 5. 返回响应
        return http.JsonResponse({'code': RETCODE.OK,
                                  'errmsg': '下单成功',
                                  'order_id': order.order_id,
                                  'payment_amount': order.total_amount})


'''
后端逻辑
    1. 获取订单编号和总金额
    2. 重新组织context返回给前端
    3. 重定向到支付成功页面

请求方式和路径
GET    	  orders/success/
'''


class OrderSuccessView(LoginRequiredMixin, View):
    def get(self, request):
        # 1. 获取订单编号, 总金额, 支付方式
        order_id = request.GET.get('order_id')
        payment_amount = request.GET.get('payment_amount')
        pay_method = request.GET.get('pay_method')

        # 2. 重新组织context返回给前端
        context = {
            'order_id': order_id,
            'payment_amount': payment_amount,
            'pay_method': pay_method
        }
        # 3. 重定向到支付成功页面
        return render(request, 'order_success.html', context)


# 订单页面的展示
class UserOrderInfoView(LoginRequiredMixin, View):
    def get(self, request, page_num):
        # 获取登录用户信息
        user = request.user
        # 查询该用户所有订单
        orders = user.orderinfo_set.all().order_by('-create_time')
        # 遍历所有订单
        for order in orders:
            # 订单状态码-1就是中文描述的下标
            order.status_name = OrderInfo.ORDER_STATUS_CHOICES[order.status - 1][1]
            order.pay_method_name = OrderInfo.PAY_METHOD_CHOICES[order.pay_method - 1][1]
            order.sku_list = []
            # 查询所有商品
            order_goods = order.skus.all()
            for order_good in order_goods:
                sku = order_good.sku
                sku.count = order_good.count
                sku.total_amount = sku.count * order_good.price
                order.sku_list.append(sku)

        # 分页
        page_num = int(page_num)
        try:
            paginator = Paginator(orders, 2)
            page_orders = paginator.page(page_num)
            total_page = paginator.num_pages
        except EmptyPage:
            return http.HttpResponseNotFound('订单不存在')

        # 组织返回数据
        context = {
            "page": page_orders,
            'total_page': total_page,
            'page_num': page_num,
            'username': user.username
        }

        return render(request, 'user_center_order.html', context)


# 评价页面的展示
class OrderCommentView(LoginRequiredMixin, View):
    # 评价页面的展示
    def get(self, request):
        # 接受参数
        order_id = request.GET.get('order_id')
        try:
            # 校验参数
            OrderInfo.objects.get(order_id=order_id, user=request.user)
        except OrderInfo.DoesNotExist:
            return http.HttpResponseBadRequest('订单不存在')

        try:
            # 查询没有评价的商品信息
            uncommit_goods = OrderGoods.objects.filter(order_id=order_id, is_commented=False)
        except OrderGoods.DoesNotExist:
            return http.HttpResponseBadRequest('商品信息有错误')

        # 构造待评价商品数据
        skus = []
        for goods in uncommit_goods:
            skus.append({
                'order_id': goods.order.order_id,
                'sku_id': goods.sku_id,
                'default_image_url': goods.sku.default_image.url,
                'name': goods.sku.name,
                'price': str(goods.sku.price),
                'score': goods.score,
                'comment': goods.comment,
                'is_anonymous': str(goods.is_anonymous),
            })

        # 渲染模板
        context = {
            'skus': skus
        }

        return render(request, 'goods_judge.html', context)


    # 商品评价的提交
    def post(self, request):
        # 获取参数
        json_dict = json.loads(request.body.decode())
        order_id = json_dict.get('order_id')
        sku_id = json_dict.get('sku_id')
        comment = json_dict.get('comment')
        score = json_dict.get('score')
        is_anonymous = json_dict.get('is_anonymous')

        # 校验参数
        if not all([order_id, sku_id, comment, score]):
            return http.HttpResponseBadRequest('参数不全')

        try:
            # 校验订单号
            OrderInfo.objects.get(order_id=order_id)
        except OrderInfo.DoesNotExist:
            return http.HttpResponseBadRequest('订单不存在')

        try:
            # 校验商品号
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseBadRequest('商品不存在')

        if is_anonymous:
            # 判断是否评价是不是bool
            if not isinstance(is_anonymous, bool):
                return http.HttpResponseBadRequest('参数错误')

        # 保存订单评价信息
        OrderGoods.objects.filter(order_id=order_id,
                                  sku_id=sku_id,
                                  is_commented=False).update(
            comment=comment,
            score=score,
            is_anonymous=is_anonymous,
            is_commented=True
        )

        # 累计评论数据
        sku.comments += 1
        sku.save()
        sku.spu.comments += 1
        sku.spu.save()

        # 如果所有订单商品都已评价，则修改订单状态为已完成
        if OrderGoods.objects.filter(order_id=order_id, is_commented=False).count() == 0:
            OrderInfo.objects.filter(order_id=order_id).update(status=OrderInfo.ORDER_STATUS_ENUM['FINISHED'])

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '评价成功'})